#include "Acceptor.h"

#include "InetAddress.h"
#include "../reactor/EventLoop.h"
#include "SocketUtils.h"
#include "../../base/log/Logging.h"

#include <cerrno>
#include <cstring>
#include <fcntl.h>

Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
        : loop_(loop),
        //  acceptSocket_(::socket(listenAddr.family(), SOCK_STREAM, 0)),
          acceptSocket_(sockets::createNonblockingOrDie(listenAddr.family())),
          acceptChannel_(loop, acceptSocket_.fd()),
          listening_(false),
          idleFd_(open("/dev/null", O_RDONLY | O_CLOEXEC)) {
    assert(idleFd_ >= 0);
    // 打开空的文件描符
    acceptSocket_.bindAddress(listenAddr);
    // 注册好socket
    acceptChannel_.setReadCallback(
            std::bind(&Acceptor::handleRead, this));
    // 绑定channel对应的回调函数
}

Acceptor::~Acceptor() { close(idleFd_); }
// 释放预先申请的文件描述符

void Acceptor::listen() {
    loop_->assertInLoopThread();
    listening_ = true;
    acceptSocket_.listen();
    // 当程序可读的时候就会调用我们定义的可读函数了
    acceptChannel_.enableReading();
    // TODO:这里设置完成要关心可读事件之后, Channel会调用update将自己添加到loop的监听事件群中
}

void Acceptor::handleRead() {
    loop_->assertInLoopThread();
    InetAddress peerAddr(0);
    // 要接收信息的客户端
    int client_sock = acceptSocket_.accept(&peerAddr);

    /* if (client_sock == -1){
         // 连接失败, 不调用方法了
         LOG_ERROR << "accept 调用异常, 原因 : " << strerror(errno);
         return;
     }
     // 将保存的客户端信息发送到回调函数
     if(client_sock >= 0){
         newConnectionCallback_(client_sock, peerAddr);
         // 这里的newConnectionCallback_的提供者是TcpServer;
     }else{
         close(client_sock);
     }*/

    if (client_sock >= 0) {
        // string hostport = peerAddr.toIpPort();
        // LOG_TRACE << "Accepts of " << hostport;
        if (newConnectionCallback_) {
            newConnectionCallback_(client_sock, peerAddr);
        } else {
            sockets::close(client_sock);
        }
    } else {
        LOG_SYSERR << "in Acceptor::handleRead";
        // Read the section named "The special problem of
        // accept()ing when you can't" in libev's doc.
        // By Marc Lehmann, author of libev.
        if (errno == EMFILE) {
            LOG_SYSERR << "文件描述符耗尽, 关闭占位文件描述符预处理";
            // 此时无法建立连接, 文件描述符耗尽, 关闭预先分配的文件描述符来处理
            ::close(idleFd_);
            idleFd_ = ::accept(acceptSocket_.fd(), NULL, NULL);
            // 建立连接, 关闭文件描述符再重庆申请一个空文件描述符
            ::close(idleFd_);
            idleFd_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
        }
    }
}